package cn.edu.pku.ss.situationperceiver;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

import android.content.Context;

public class SituationReasoner {

	private static HashMap<Integer,Situation> situations;
	private List<Relation> relations;
	private List<Relation> incompatibles;
	private List<Integer> situationPool = new ArrayList<Integer>();
	private List<Integer> predicateSet = new ArrayList<Integer>();
	private DBAdapter dbAdapter;
	private ReasoningRecord record;
	
	public static Situation getSituation(int id)
	{
		return situations.get(id);
	}
	
    public void startSRA(Context context) 
    {
        readSR();
        predicateSet.add(1);
        dbAdapter = new DBAdapter(context);
        new Thread(new SRAThread()).start();
    }
    
    private void readSR()
    {
    	try {
    		SAXParserFactory factory = SAXParserFactory.newInstance();
			SAXParser parser = factory.newSAXParser();
			XMLReader reader = parser.getXMLReader();
			RSHandler handler = new RSHandler();
			reader.setContentHandler(handler);
			InputSource is = new InputSource(this.getClass().getResourceAsStream("situationStructure.xml"));
			reader.parse(is);
			situations = handler.getSituations();
			relations = handler.getRelations();
			incompatibles = handler.getIncompatibles();
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}	
    }
    
    class SRAThread implements Runnable{
    	
    	private List<Integer> deductiveNodes = new ArrayList<Integer>();
    	private List<Integer> validateNodes = new ArrayList<Integer>();
    	
    	private void resetSituations()
    	{
    		Set<Integer> set = situations.keySet();
    		Iterator<Integer> itr = set.iterator();
    		while (itr.hasNext()) {
				int id = itr.next();
				Situation situation = situations.get(id);
				situation.setVerified(false);
    		}
    	}
    	
    	private boolean checkTrigger()
    	{
    		situationPool.clear();
    		Set<Integer> set = situations.keySet();
    		Iterator<Integer> itr = set.iterator();
    		while (itr.hasNext()) {
				int id = itr.next();
				Situation situation = situations.get(id);
				if(situation.isVerified() && situation.isExistence())
				{
			        situationPool.add(id);
			        record.tiggerID = id + "";
			        record.intermediateID = "";
			        return true;
				}
			}
    		return false;
    	}
    	
    	@Override
    	public void run() {
    		// TODO Auto-generated method stub
    		while(true)
    		{
    			if(checkTrigger())
    			{
    				startReasoning();
    				resetSituations();
    			}
    			else
    			{
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
    			}
    		}
    	}
    	
    	private void startReasoning()
    	{
    		long begin = System.currentTimeMillis();
    		while(!checkEnding())
    		{
    			getDeductiveNodes();
        		getValidateNodes();
        		verifyValidateNodes();
        		long end = System.currentTimeMillis();
        		if(end - begin > 100*1000)
        			break;
    		}
    		
    		record.resultID = "";
    		Iterator<Integer> itr = situationPool.iterator();
    		while(itr.hasNext())
    			record.resultID += itr.next() + " ";
    		
    		dbAdapter.open();
    		dbAdapter.insert(record);
    		dbAdapter.close();
    	}
    	
    	private boolean checkEnding()
    	{
    		if(predicateSet.isEmpty())
    			return false;
    		else
    		{
    			Iterator<Integer> itr = predicateSet.iterator();
    			while(itr.hasNext())
    				if(!inList(itr.next(), situationPool))
    					return false;
    			return true;
    		}
    	}
    	
    	private boolean hasConflict(int one, int another)
    	{
    		Iterator<Relation> itr = incompatibles.iterator();
    		while(itr.hasNext())
    		{
    			Relation relation = itr.next();
    			if((relation.getPrevious() == one && relation.getNext() == another) ||
    					(relation.getPrevious() == another && relation.getNext() == one))
    				return true;
    		}
    		return false;
    	}
    	
    	private int clipNodeSuccess(int deductive, int pos)
    	{
    		List<Integer> clipNodes = new ArrayList<Integer>();
    		for(int i = 0; i < deductiveNodes.size(); i++)
    		{
    			int deductiveNode = deductiveNodes.get(i);
    			if(hasRelation(deductiveNode, deductive) ||
    					hasConflict(deductiveNode, deductive))
    			{
    				deductiveNodes.remove(i);
    				clipNodes.add(deductiveNode);
    				i--;
    			}
    		}
    		
    		for(int j = 0; j < validateNodes.size(); j++)
    		{
    			int validateNode = validateNodes.get(j);
    			if(hasRelation(validateNode, deductive) && 
    					situations.get(validateNode).isTransparent())
    			{
    				validateNodes.remove(j);
    				j--;
    				continue;
    			}
    			
    			Iterator<Integer> itr = clipNodes.iterator();
    			while (itr.hasNext()) 
    			{
    				int clip = itr.next();
    				if(hasRelation(validateNode, clip))
    				{
    					validateNodes.remove(j);
    					j--;
    					break;
    				}
				}
    		}
    		
    		for(int k = 0; k < validateNodes.size(); k++)
    		{
    			int validateNode = validateNodes.get(k);
    			Situation situation = situations.get(validateNode);
    			if(!situation.isVerified())
    				return k-1;
    		}
    		return pos;
    	}
    	
    	private int checkDeduction(int validate, int pos)
    	{
    		int deductiveNode = -1;
    		Iterator<Integer> itr = deductiveNodes.iterator();
    		while (itr.hasNext()) {
    			deductiveNode = itr.next();
    			Situation situation = situations.get(deductiveNode);
    			if(!situation.isVerified())
    				break;
			}
    		if(deductiveNode == -1)
    			return pos;
    		
    		if(!situations.get(validate).isTransparent())
    		{
    			Iterator<Relation> it = relations.iterator();
        		while(it.hasNext())
        		{
        			Relation relation = it.next();
        			if(relation.getNext() == deductiveNode &&
        					!situations.get(relation.getPrevious()).isVerified())
        				return pos;
        		}
    		}
    		
    		situations.get(deductiveNode).setVerified(true);
    		situations.get(deductiveNode).setExistence(true);
    		return clipNodeSuccess(deductiveNode, pos);
    	}
    	
    	private boolean hasRelation(int validate, int deductive)
    	{
    		Iterator<Relation> itr = relations.iterator();
    		while(itr.hasNext())
    		{
    			Relation relation = itr.next();
    			if(relation.getPrevious() == validate && 
    					relation.getNext() == deductive)
    				return true;
    		}
    		return false;
    	}
    	
    	private int clipNodeFailure(int validate, int pos)
    	{
    		List<Integer> clipNodes = new ArrayList<Integer>();
    		for(int i = 0; i < deductiveNodes.size(); i++)
    		{
    			int deductiveNode = deductiveNodes.get(i);
    			if(hasRelation(validate, deductiveNode))
    			{
    				deductiveNodes.remove(i);
    				clipNodes.add(deductiveNode);
    				i--;
    			}
    		}
    		
    		for(int j = 0; j < validateNodes.size(); j++)
    		{
    			int validateNode = validateNodes.get(j);
    			Iterator<Integer> itr = clipNodes.iterator();
    			while (itr.hasNext()) {
    				int clip = itr.next();
    				if(hasRelation(validateNode, clip))
    				{
    					validateNodes.remove(j);
    					j--;
    					break;
    				}
				}
    		}
    		
    		for(int k = 0; k < validateNodes.size(); k++)
    		{
    			int validateNode = validateNodes.get(k);
    			Situation situation = situations.get(validateNode);
    			if(!situation.isVerified())
    				return k-1;
    		}
    		return pos;
    	}
    	
    	private boolean endChildrenFinding(List<Integer> validateNodes)
    	{
    		Iterator<Integer> itr = validateNodes.iterator();
    		while(itr.hasNext())
    		{
    			int validateNode = itr.next();
    			if(hasChildSituations(validateNode))
    				return false;
    		}
    		return true;
    	}
    	
    	private boolean hasChildSituations(int validateNode)
    	{
			Iterator<Relation> itr = relations.iterator();
    		while(itr.hasNext())
    		{
    			Relation relation = itr.next();
    			if(relation.getNext() == validateNode)
    			{
    				return true;
    			}
    		}
    		return false;
    	}
    	
    	private List<Integer> getAtomSituations(int validateNode)
    	{
    		List<Integer> atoms = new ArrayList<Integer>();
    		atoms.add(validateNode);
    		while(!endChildrenFinding(atoms))
    		{
    			List<Integer> children = new ArrayList<Integer>();
    			Iterator<Integer> itra = atoms.iterator();
        		while(itra.hasNext())
        		{
        			int node = itra.next();
        			if(hasChildSituations(node))
        			{
        				Iterator<Relation> itr = relations.iterator();
                		while(itr.hasNext())
                		{
                			Relation relation = itr.next();
                			if(relation.getNext() == node &&
                					situations.get(relation.getPrevious()).isTransparent())
                			{
                				children.add(relation.getPrevious());
                				break;
                			}
                			else if(relation.getNext() == node)
                			{
                				children.add(relation.getPrevious());
                			}
                		}
        			}
        			else
        				children.add(node);
        		}
        		atoms.clear();
        		atoms.addAll(children);
    		}
    		return atoms;
    	}
    	
    	private boolean situationVerified(List<Integer> atoms)
    	{
    		Iterator<Integer> itr = atoms.iterator();
    		while(itr.hasNext())
    		{
    			int atom = itr.next();
    			Situation situation = situations.get(atom);
    			if(!situation.isVerified())
    				return false;
    		}
    		return true;
    	}
    	
    	private boolean situationExist(List<Integer> atoms)
    	{
    		Iterator<Integer> itr = atoms.iterator();
    		while(itr.hasNext())
    		{
    			int atom = itr.next();
    			Situation situation = situations.get(atom);
    			if(!situation.isExistence())
    				return false;
    		}
    		return true;
    	}
    	
    	private void requestSituations(List<Integer> atoms)
    	{
    		Iterator<Integer> itr = atoms.iterator();
    		while(itr.hasNext())
    		{
    			int id = itr.next();
    			Situation situation = situations.get(id);
    			if(!situation.isVerified())
    				ContextInterpreter.requestSituation(id);
    		}
    	}
    	
    	private void verifyValidateNodes()
    	{
    		for(int i = 0; i < validateNodes.size(); i++) 
    		{
    			int id = validateNodes.get(i);
    			List<Integer> atoms = getAtomSituations(id);
    			requestSituations(atoms);
    			
    			int time = 50;
    			while(time >= 0)
    			{
    				if(situationVerified(atoms))
    				{
    					if(situationExist(atoms))
    						i = checkDeduction(id, i);
    					else
    						i = clipNodeFailure(id, i);
    					break;
    				}
    				
    				try {
    					time--;
						Thread.sleep(100);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
    			}
    			if(!situationVerified(atoms))
    				i = clipNodeFailure(id, i);
			}
    		
    		Iterator<Integer> itrVN = validateNodes.iterator();
    		while(itrVN.hasNext())
    			record.intermediateID += itrVN.next() + " ";
    		record.intermediateID += ",";
    		
    		situationPool.clear();
    		Iterator<Integer> itr = deductiveNodes.iterator();
    		while(itr.hasNext())
    		{
    			situationPool.add(itr.next());
    			record.intermediateID += itr.next() + " ";
    		}
    		record.intermediateID += ";";
    		
    		deductiveNodes.clear();
    		validateNodes.clear();
    	}
    	
    	private void getValidateNodes()
    	{
    		Iterator<Integer> itrDN = deductiveNodes.iterator();
    		while (itrDN.hasNext()) {
    			int deductive = itrDN.next();
    			Iterator<Relation> itrR = relations.iterator();
    			while (itrR.hasNext()) {
					Relation relation = itrR.next();
					if(relation.getNext() == deductive && 
							!inList(relation.getPrevious(), situationPool) &&
							!inList(relation.getPrevious(), validateNodes))
						validateNodes.add(relation.getPrevious());
				}
			}
    	}
    	
    	private boolean inList(int element, List<Integer> list)
    	{
    		Iterator<Integer> itr = list.iterator();
    		while (itr.hasNext()) {
    			int i = itr.next();
    			if(element == i)
    				return true;
			}
    		return false;
    	}
    	
    	private void getDeductiveNodes()
    	{
    		Iterator<Integer> itrSP = situationPool.iterator();
    		while (itrSP.hasNext()) {
    			int element = itrSP.next();
    			Iterator<Relation> itrR = relations.iterator();
    			while (itrR.hasNext()) {
					Relation relation = itrR.next();
					if(relation.getPrevious() == element)
						deductiveNodes.add(relation.getNext());
				}
			}
    		removeDuplicate(deductiveNodes);
    		sortDeductiveNodes();
    	}
    	
    	private void removeDuplicate(List<Integer> list)
    	{
    		HashSet<Integer> h = new HashSet<Integer>(list);
    		list.clear();
    		list.addAll(h);
    	}
    	
    	private void sortDeductiveNodes()
    	{
    		DeductiveComparator comparator = new DeductiveComparator();
    		Collections.sort(deductiveNodes, comparator);
    	}
    	
    	private long getRC(int deductive)
    	{
    		long rc = 0;
    		Iterator<Relation> itr = relations.iterator();
    		while(itr.hasNext())
    		{
    			Relation relation = itr.next();
    			if(relation.getNext() == deductive)
    				rc += situations.get(relation.getPrevious()).getRc();
    		}
    		return rc;
    	}
    	
    	class DeductiveComparator implements Comparator<Integer>
    	{
			@Override
			public int compare(Integer lhs, Integer rhs) {
				// TODO Auto-generated method stub
				long lrc = getRC(lhs.intValue());
				long rrc = getRC(rhs.intValue());
				if(lrc > rrc)
					return 1;
				else
					return -1;
			}	
    	}
    }
}

